package graph_viewer.experiment;

import java.awt.*;
import java.util.*;

import graph_viewer.app_exception.*;
import graph_viewer.canvas.*;
import graph_viewer.structure.*;
import graph_viewer.util.*;

public class  Experiment_Metrics  implements Experiment_Metrics_Interface{	

	Graph	the_graph;
	double	the_initial_aspect_ratio;
	long		the_operation_time;
	Abridgement 
		the_compared_abridgement;

	double			
		the_width, the_height;

	double the_edge_ratio;
	
	int	the_visual_node_number;

	ArrayList
		the_x_orthogonal_order,
		the_y_orthogonal_order;
	
	String layout_name, adjuster_name;

	String focus_label, picked_focus;

	public Experiment_Metrics( Drawer_Canvas a_graph_canvas, double a_initial_aspect_ratio, long an_operation_time, Abridgement an_abridgement ){
		initialize( a_graph_canvas );

		the_initial_aspect_ratio		= a_initial_aspect_ratio;
		the_operation_time			= an_operation_time;
		the_compared_abridgement	= an_abridgement;

		Debug.println( "Experiment_Metrics.the_initial_aspect_ratio set =" + the_initial_aspect_ratio, 38 );
	}
	
	public Experiment_Metrics( Drawer_Canvas a_graph_canvas, long an_operation_time, Abridgement an_abridgement ){
		initialize( a_graph_canvas );
													       
		the_initial_aspect_ratio		= 1;
		the_operation_time			= an_operation_time;
		the_compared_abridgement	= an_abridgement;

		Debug.println( "Experiment_Metrics.the_initial_aspect_ratio set =" + the_initial_aspect_ratio, 38 );
	}

	public Experiment_Metrics( Drawer_Canvas a_graph_canvas ){
		initialize( a_graph_canvas );		
	}

	public Experiment_Metrics( Drawer_Canvas a_graph_canvas, Abridgement an_abridgement ){
		initialize( a_graph_canvas );		
		the_compared_abridgement	= an_abridgement;
	}

	//=======================================
	// public methods
	//=======================================
	public int find_overlapping_number(){
		return Layout_Util.find_overlapping_number( the_graph );
	}
	
	public double[] find_root_size(){		

		Node root	 = the_graph.find_tree().get_root();

		Node_Position	 rt_np	= root.get_position();
		double
			width	= rt_np.get_width(),
			height	= rt_np.get_height();

		double[] root_size	= new double[]{ width, height };

		return root_size;
	}
	
	public double find_area(){
		double
			width	= get_width(),
			height	= get_height();

		return width * height;
	}
	
	public double find_area_node_ratio(){
		double
			area			= find_area(),
			node_num	= the_graph.find_nodes().size() - 1;

		return area/node_num;
	}
	
	public double find_L1_size(){

		return Math.max( get_width(), get_height());
	}

	public double find_aspect_ratio(){
		double aspect_ratio;

		double
			width	= get_width(),
			height	= get_height();

		if ( width < height ){
			aspect_ratio =  height/width;
		}else{
			aspect_ratio = width/height;
		}

		return aspect_ratio;
	}
	
	public double find_normalized_aspect_ratio(){
		return find_aspect_ratio()/the_initial_aspect_ratio;
	}


	public double find_edge_raito(){
		return get_edge_ratio();
	}

	public int[] compare_orthogonal_order(){		
		Debug.println( "=========Start compare_orthogonal_order============", 41 );
		
		Abridgement current_abridgement	= new Abridgement( the_graph );		
		ArrayList comparable_clusters_labels = find_comparable_clusters_labels( current_abridgement, the_compared_abridgement );

		Debug.println( "\n\tfind_comparable_clusters_labels: " + comparable_clusters_labels, 42 );

		int 
			x_orth_diff	= 0,
			y_orth_diff	= 0;

		for ( Iterator _i = comparable_clusters_labels.iterator(); _i.hasNext(); ){
			String a_label	= (String)_i.next();

			Abridgement_Node an_abridgement_node			= current_abridgement.search( a_label );
			Abridgement_Node an_compared_abridgement_node	= the_compared_abridgement.search( a_label );
		
			if ( an_abridgement_node.get_is_closed() || an_compared_abridgement_node.get_is_closed() ){
				Debug.println( "\n\t" + a_label + " is closed in either of the two abridgment.", 41 );

				continue;
			}
			
			Debug.println( "\n\tCompare nodes under cluster_" + a_label, 42 );
			ArrayList labels_of_nodes_to_compare	 = find_common_strings( an_abridgement_node.get_children_labels(), an_compared_abridgement_node.get_children_labels() );

			int[] orth_diff = do_compare_orthogonal_order_impl( labels_of_nodes_to_compare, current_abridgement, the_compared_abridgement );						

			x_orth_diff += orth_diff[0];
			y_orth_diff += orth_diff[1];
		}

		Debug.println( "=========End compare_orthogonal_order============", 41 );
		
		int[] order_diff = new int[]{ x_orth_diff, y_orth_diff };

		return order_diff;
	}
	
	public String to_static_string(){
		int[] orth_diff		= compare_orthogonal_order();

		int total_orth_diff	= orth_diff[0] + orth_diff[1];
		StringBuffer metrics	= new StringBuffer();
		metrics.append(	adjuster_name + 
						"," + the_visual_node_number + 
						"," + find_area() + 
						"," + find_L1_size() + 
						"," + find_aspect_ratio() +
						"," + find_edge_raito() +    			
						"," + the_operation_time +
						"," + find_overlapping_number() +
						"," + total_orth_diff
					);
		
		
		//metrics.append( "\n\tOrthogonal Order: x_diff " + orth_diff[0] + ", y_diff " + orth_diff[1] );

		//metrics.append( "\n\tthe_initial_aspect_ratio="  +  the_initial_aspect_ratio );
		//metrics.append( "\n\tAspect_ratio="  +  find_aspect_ratio() );
		//metrics.append( "\n\tNormalized_Aspect_ratio="  +  find_normalized_aspect_ratio() );

		//metrics.append( "\n\tOrthogonal Order: " );
		//metrics.append( "\n\t\tx direction sort: " + get_x_orth_order() );
		//metrics.append( "\n\t\ty direction sort: " + get_y_orth_order() );
		//metrics.append( "\n====================================="  );

		return metrics.toString();
	}
	
	public String to_dynamic_string(){
		int[] orth_diff		= compare_orthogonal_order();

		int total_orth_diff	= orth_diff[0] + orth_diff[1];

		StringBuffer metrics	= new StringBuffer();
		//metrics.append( "\n===========Experiment_Metrics==============\n" );
		//metrics.append( "\tLayout Name: " + layout_name );
		//metrics.append( "\n\tAdjuster Name: " + adjuster_name );
		//metrics.append( "\n\tRoot Size: width-" + get_width() + ", height-" + get_height() + ", at "+ the_graph.find_tree().get_root().get_position() + "\n");

		//metrics.append( "\n\tVisualNode,\t\tFocus,\t\tOverlapping_Num\n" );
		//metrics.append( "\t" + the_visual_node_number  + ",\t\t\t" + focus_label + ",\t\t\t" + find_overlapping_number()+ "\n" );
		
		//metrics.append("\n\tFocus,\t\tVisualNode,\t\tarea,\t\tL1_size,\tAspect_ratio,\tNormalized_Aspect_ratio,\toperation_time,\toverlapping" );
		metrics.append(	adjuster_name 
						+ ",_" + focus_label 
						+ "_, " + the_visual_node_number
						+ "," + find_area()
						+ "," + find_aspect_ratio() 
						+ "," + find_L1_size()	  			
						+ "," + find_edge_raito()    
						+ "," + the_operation_time
						+ "," + find_spring_involved_clusters_number()
						//+ "," + total_orth_diff
						//+ "," + find_overlapping_number() 
					);

		return metrics.toString();
	}
	//=======================================
	// setter & getter
	//=======================================
	public void set_height( double a_height ){
		this.the_height	 = a_height;
	}

	public double get_height(){
		return this.the_height;
	}

	public void set_width( double a_width ){
		this.the_width	= a_width;
	}

	public double get_width(){
		return this.the_width;
	}

	public void set_edge_ratio( double an_edge_ratio ){
		this.the_edge_ratio	 = an_edge_ratio;
	}
	
	public double get_edge_ratio(){
		return this.the_edge_ratio;
	}

	public void set_visual_node_number( int a_visual_node_number ){
		the_visual_node_number = a_visual_node_number;
	}
	
	public int get_visual_node_number(){
		return the_visual_node_number;
	}
	
	public void set_x_orth_order( ArrayList a_orth_order ){
		this.the_x_orthogonal_order = a_orth_order;
	}

	public ArrayList get_x_orth_order(){
		return this.the_x_orthogonal_order ;
	}

	public void set_y_orth_order( ArrayList a_orth_order ){
		this.the_y_orthogonal_order = a_orth_order;
	}

	public ArrayList get_y_orth_order(){
		return this.the_y_orthogonal_order ;
	}


	//====================================================
	//protected methods
	//====================================================	
	// the elmentes in two @list are same, but the order of the elements can be different.
	//@return the labels of ordered leaft_clusters
	protected ArrayList build_orth_list( Graph a_graph, double direction ){

		ArrayList leaf_clusters	= a_graph.find_leaf_clusters();
		ArrayList orth_list	 = new ArrayList();

			Layout_Util.x_direction_sort( leaf_clusters );
			Layout_Util.y_direction_sort( leaf_clusters );

		if ( direction == Public_Values.X_AXIS ){
			Layout_Util.x_direction_sort( leaf_clusters );
		}else if ( direction == Public_Values.Y_AXIS ){
			Layout_Util.y_direction_sort( leaf_clusters );
		}else{
			throw new App_Exception( "Invalid direction value found when adjust layout!");
		}		
				
		for ( Iterator _i = leaf_clusters.iterator(); _i.hasNext(); ){
			Node a_node	= (Node)_i.next();

			String a_node_label	= a_node.get_label();

			orth_list.add( a_node_label );
		}

		return orth_list;
	}
	
	//====================================================
	//private methods
	//====================================================
	private void initialize(  Drawer_Canvas a_graph_canvas ){
		Clustered_Graph a_graph	= (Clustered_Graph)a_graph_canvas.get_graph();

		the_graph		= new Clustered_Graph( a_graph );
		layout_name		= new String( a_graph_canvas.get_layout().get_name() );
		adjuster_name	= new String( a_graph_canvas.get_layout_adjuster().get_name() );
		
		the_visual_node_number
						= a_graph.find_displayed_nodes().size();

		Node focus		= a_graph_canvas.get_canvas_metrics().get_focus();	
		this.focus_label	= focus.get_label();
		
		double[] root_size	= find_root_size();
		set_width( root_size[0] );
		set_height( root_size[1] );

		set_edge_ratio( calculate_edge_raito() );
		build_x_direction_orthogonal_list();
		build_y_direction_orthogonal_list();
	}

	private void build_x_direction_orthogonal_list(){	
		ArrayList x_orth_list	 = build_orth_list( the_graph, Public_Values.X_AXIS );
		
		set_x_orth_order( x_orth_list );
	}

	private void build_y_direction_orthogonal_list(){		
		ArrayList y_orht_list	 = build_orth_list( the_graph, Public_Values.Y_AXIS );

		set_y_orth_order( y_orht_list ); 
	}

	//@return longest_edge/shortest/edge
	private double calculate_edge_raito(){
		double 
			longest_edge	= 0,
			shortest_edge = Public_Values.MAX_INT;

		ArrayList edges	= Structure_Util.find_displayed_edges( the_graph );//the_graph.find_edges();//label pairs
		
		for ( Iterator _i =edges.iterator(); _i.hasNext(); ){
			String[] labels	= (String[])_i.next();

			String
				from	= labels[0],
				to	= labels[1];

			Edge	an_edge	= new Edge( the_graph.search(from), the_graph.search( to) );

			double edge_length	 = an_edge.find_length();

			if ( edge_length > longest_edge ){
				longest_edge = edge_length;
			}

			if ( edge_length < shortest_edge ){
				shortest_edge	= edge_length;
			}
		}
		
		Debug.println( "\t=======longest_edge" + longest_edge + ", shortest_edge" + shortest_edge, 55 );
		return longest_edge/shortest_edge;
	}

	//compare the orthogonal order of the two node's children
	//@return [0] x_diff, [1] y_diff
	private int[] do_compare_orthogonal_order_impl( ArrayList labels_of_nodes_to_compare, Abridgement current_abridgement, Abridgement the_compared_abridgement ){
		
		//to_do: compare nodes positions
		ArrayList 
			abridgement_node_in_current_abridgement		= find_abridgement_nodes_by_labels( labels_of_nodes_to_compare, current_abridgement  ),
			abridgement_node_in_compared_abridgement	= find_abridgement_nodes_by_labels( labels_of_nodes_to_compare, the_compared_abridgement  );
		
		int
			x_diff	= do_x_comparsion( abridgement_node_in_current_abridgement, abridgement_node_in_compared_abridgement),
			y_diff	= do_y_comparsion( abridgement_node_in_current_abridgement, abridgement_node_in_compared_abridgement);
		
		return new int[]{ x_diff, y_diff };
	}
	
	private int do_x_comparsion( ArrayList base_abridgement_node_list, ArrayList target_abridgement_node_list ){
		Layout_Util.x_direction_sort( base_abridgement_node_list );
		Layout_Util.x_direction_sort( target_abridgement_node_list );
		Debug.println( "Do_x_comparsion btw: \n" + base_abridgement_node_list, 42 );
		Debug.println( " \n" + target_abridgement_node_list, 42 );
		
		int x_diff	= do_comparsion( base_abridgement_node_list,  target_abridgement_node_list );

		Debug.println( "\n=========x_diff: " + x_diff, 42 );
		
		return x_diff;
	}

	private int do_y_comparsion( ArrayList base_abridgement_node_list, ArrayList target_abridgement_node_list ){
		Layout_Util.y_direction_sort( base_abridgement_node_list );
		Layout_Util.y_direction_sort( target_abridgement_node_list );

		Debug.println( "Do_y_comparsion btw: \n" + base_abridgement_node_list, 42 );
		Debug.println( " \n" + target_abridgement_node_list, 42 );	
		
		int y_diff	= do_comparsion( base_abridgement_node_list,  target_abridgement_node_list );

		Debug.println( "\n=========y_diff: " + y_diff, 42 );
		
		return y_diff;
	}


	private ArrayList extract_label_list( ArrayList abridgement_node_list ){
		ArrayList node_label_list	= new ArrayList();

		for ( Iterator _i = abridgement_node_list.iterator(); _i.hasNext(); ){
			Abridgement_Node a_n	= (Abridgement_Node)_i.next();
			
			node_label_list.add( a_n.get_label() );
		}
		
		return node_label_list;
	}
	
	//@param	abridgement_node lists
	private int do_comparsion( ArrayList base_abridgement_node_list, ArrayList target_abridgement_node_list ){
		ArrayList
			base_node_label_list	= extract_label_list( base_abridgement_node_list ),
			target_node_labe_list	= extract_label_list( target_abridgement_node_list );

		

		int orth_difference	= 0;

		int
			base_list_size			= base_abridgement_node_list.size(),
			compared_list_size	 	= target_abridgement_node_list.size();
		
		if (  base_list_size != compared_list_size ){			
			throw new App_Exception( "Invalid compared orthogonal list!");
		}

		for ( int i = 0 ; i < base_list_size; i++ ){
			String
				base_list_label		= (String)base_node_label_list.get( i );
			
			int poistion_at_target_list	 = target_node_labe_list.indexOf( base_list_label );
			
			Debug.println( base_list_label + " position ", 42 );
			Debug.println( "\tin base_list: " + i, 42 );
			Debug.println( "\tin target_list:" + poistion_at_target_list, 42 );

			int difference	= Math.abs( i - poistion_at_target_list );
			Debug.println( "\tdifference: \t" + difference, 42 );

			orth_difference += difference * difference;
		}

		return orth_difference;
	}

	

	//abridgement_node lists
	private ArrayList find_abridgement_nodes_by_labels( ArrayList labels, Abridgement an_abridgement ){
		ArrayList abridgement_nodes	= new ArrayList();

		for ( Iterator _i = labels.iterator(); _i.hasNext(); ){
			String a_label	= (String)_i.next();

			Abridgement_Node a_n	= an_abridgement.search( a_label );

			abridgement_nodes.add( a_n );
		}

		return abridgement_nodes;
	}
	
	//@param	sorted label lists
	private int compare_orthogonal_relation( ArrayList base_list, ArrayList target_list ){
		int orth_difference	= 0;

		int
			base_list_size			= base_list.size(),
			compared_list_size	 	= target_list.size();
		
		if (  base_list_size != compared_list_size ){			
			throw new App_Exception( "Invalid compared orthogonal list!");
		}

		for ( int i = 0 ; i < base_list_size; i++ ){
			String base_list_label		= (String)base_list.get( i );
			
			int poistion_at_compared_list	 = target_list.indexOf( base_list_label );
			
			Debug.println( base_list_label + " position ", 6 );
			Debug.println( "\tin base_list: " + i, 6 );
			Debug.println( "\tin target_list:" + poistion_at_compared_list, 6 );

			int difference	= Math.abs( i - poistion_at_compared_list );
			Debug.println( "\tdifference: \t" + difference, 6 );

			orth_difference += difference * difference;
		}

		return orth_difference;
	}

	//the labels of the nodes displayed in both abridgement
	private ArrayList find_comparable_clusters_labels( Abridgement current_abridgement, Abridgement the_compared_abridgement ){
		ArrayList comparable_clusters_labels	= new ArrayList();

		ArrayList
			current_abridgement_nodes_labels	= current_abridgement.find_abridgement_nodes_labels(),
			compared_abridgement_nodes_labels	= the_compared_abridgement.find_abridgement_nodes_labels();

		return find_common_strings( current_abridgement_nodes_labels, compared_abridgement_nodes_labels );
	}
	
	// find the strings contained in both arraylist
	private ArrayList find_common_strings( ArrayList an_string_list, ArrayList another_string_list ){
		ArrayList common_strings	= new ArrayList();

		for ( Iterator _i = an_string_list.iterator(); _i.hasNext(); ){
			
			Object an_element	= _i.next();
			
			String expected_class_name	= new String("").getClass().getName();

			if (! an_element.getClass().getName().equals(  expected_class_name ) ) {
				throw new App_Exception( " find_common_strings from a non string list!" );
			}

			String a_string	= (String)an_element;

			Debug.println( "A label in current_abridgement_nodes_labels" + a_string, 41 );

			if ( another_string_list.contains( a_string ) ){
				Debug.println( "The label " + a_string + " is in compared_abridgement_nodes_labels", 41 );
				common_strings.add( a_string );
			}
		}

		return common_strings;
	}

	private int find_spring_involved_clusters_number(){
		Node focus_node	= the_graph.search( this.focus_label );
		Debug.println( "find_spring_involved_clusters_number with focus: " + focus_node, 57 );

		Tree the_tree = the_graph.find_tree();

		int focus_depth	= the_tree.depth( focus_node );
		Debug.println( "depth of focus: " + focus_depth, 57 );
		
		ArrayList tree_nodes	= the_tree.get_tree();
	
		int  
			upper_focus_cluster	= 0,
			sibling_clusteter		= 0,
			children_cluster		= 0;
		
		for ( Iterator _i = tree_nodes.iterator(); _i.hasNext(); ){
			Node a_node = (Node)_i.next();
			
			if (  the_tree.depth( a_node ) < focus_depth ){
				upper_focus_cluster++;
			}
		}

		Debug.println( "# of upper focus cluster : " + upper_focus_cluster, 57 );

		if ( !the_tree.is_leaf( focus_node) ){
			children_cluster = focus_node.get_children().size();
		}

		Debug.println( "# of children_cluster : " + children_cluster, 57 );
		
		if (!the_tree.is_root( focus_node)  ){
			sibling_clusteter = focus_node.get_parent().get_children().size();
		}
		Debug.println( "# of sibling_clusteter : " + sibling_clusteter, 57 );


		int spring_involved_clusters_number  = upper_focus_cluster + sibling_clusteter + children_cluster;		

		Debug.println( "# of spring_involved_clusters_number : " + spring_involved_clusters_number, 57 );

		return spring_involved_clusters_number;
	}
 }//:~